[[Attachment(파일명)]] [[Attachment(파일명, 300px)]] [[Attachment(파일명, 50%)]]
<img> 태그로 인라인 표시<a> 다운로드 링크로 표시px 또는 % 단위)attachmentFallback.svg) 표시
Attachment/{siteSeq}/{pageName}/{originalFilename}/{basename}.{timestamp}.{ext}예시:
Attachment/1/MyPage/photo.jpg/photo.20240101120000000.jpg Attachment/1/MyPage/clipboard/clipboard.20240101120000000.png
pageName과 파일명의 특수문자는 [^\p{IsHangul}\p{IsHan}\p{IsHiragana}\p{IsKatakana}a-zA-Z0-9._-] 패턴으로 _ 치환{pageName}/clipboard/ 하위에 저장이 레이아웃은 logics.AttachmentLogic 한 곳에서만 조립한다. Root, sitePrefix, pagePrefix, sanitizePathSegment 가 그 자리다.
키를 쓰는 쪽과 읽는 쪽이 프리픽스를 각자 조립하면, 어긋나도 컴파일은 통과하고 **첨부가 사라진 뒤에야** 드러난다. 실제로 치환 함수가 Api·ApiV1·Wiki·MacroAttachment 에 네 벌 있었다.
컬럼 | 설명 |
|---|---|
seq | PK |
site | 사이트 seq |
pageName | 첨부된 페이지 이름 |
user | 업로드 사용자 seq (nullable) |
uploaderEmail | 업로드자 이메일 (nullable) |
originalFilename | 원래 파일명 |
storedFilename | S3에 저장된 파일명 |
bucket | S3 버킷 이름 |
objectKey | S3 오브젝트 키 |
contentType | MIME 타입 |
fileSize | 파일 크기 (bytes) |
status | Initiated / Uploaded / Failed / Deleted |
etag | S3 ETag (업로드 후 저장) |
dateInserted | 레코드 생성 일시 |
dateUploaded | S3 업로드 완료 일시 |
dateUpdated | 최근 변경 일시 |
dateDeleted | 삭제 일시 (soft delete) |
Initiated → Uploaded (S3 업로드 성공)
→ Failed (S3 업로드 실패)
Uploaded → Deleted (삭제 처리)POST /api/uploadAttachment Content-Type: multipart/form-data 파라미터: pageName (text) - 첨부할 페이지 이름 file (file) - 업로드할 파일 응답 (JSON): objectKey - S3 오브젝트 키 attachmentMacro - 바로 사용 가능한 매크로 문자열 예: [[Attachment(파일명)]] fileUrl - presigned URL (24시간 유효) contentType - MIME 타입
GET /api/pageAttachments?pageName={pageName}
응답 (JSON):
attachments: [
{
objectKey - S3 오브젝트 키
originalFilename
contentType
fileSize
fileUrl - presigned URL
integrityStatus - OK / DB_ONLY / S3_ONLY
attachmentMacro - 매크로 문자열
}
]integrityStatus) 제공POST /api/deleteAttachment Content-Type: application/x-www-form-urlencoded 파라미터: pageName - 페이지 이름 objectKey - S3 오브젝트 키 (전체 경로 또는 상대 경로) 응답 (JSON): ok - true objectKey - 삭제된 오브젝트 키
POST /api/uploadClipboardImage Content-Type: multipart/form-data 파라미터: pageName - 페이지 이름 file - 이미지 파일 (Content-Type이 image/* 여야 함) 응답 (JSON): objectKey attachmentMacro imageUrl
play.http.parser.maxMemoryBuffer(2MB) 초과 시 413이 발생하여 multipart로 변경Attachment/{siteSeq}/{pageName}/ 프리픽스로 최대 200개 조회하여 삭제AttachmentLogic.deletePageAttachments 하나다.AWS_REGION·AWS_ACCESS_KEY_ID·AWS_SECRET_ACCESS_KEY·bucket 중 하나라도 비어 있으면 S3 작업을 건너뛴다. 첨부는 "없음"으로 취급하고 DB 정리는 그대로 진행한다.
이 가드는 원래 ApiV1 에만 있었다. 같은 함수가 Wiki 에도 한 벌 더 있었고 그쪽에는 가드가 없어서, S3 미설정 상태에서는 빈 자격증명·빈 리전으로 클라이언트를 만들어 호출했다. 사본이 둘이면 한쪽만 고쳐도 컴파일이 통과한다는 것의 실례다. 합치면서 가드 있는 쪽으로 통일했다.
logics.S3Logic 이 자격증명별로 캐시해 돌려준다.
AmazonS3 는 커넥션 풀을 들고 있고 공유하도록 만들어진 객체다. 컨트롤러 3곳이 각자 buildAmazonS3Client() 를 갖고 **요청마다 새로 만들고 있었다**(호출부 11곳). 만든 클라이언트를 닫는 곳은 없었으므로 풀이 요청마다 쌓였다. 어디서도 클라이언트를 닫거나 상태를 바꾸지 않으므로 자격증명당 한 개를 공유해도 안전하다.
Attachment/{siteSeq}/{pageName}/ 경로)Similar pages by cosine similarity. Words after page name are term frequency.